import {PresetName} from "@lodestar/params";

/**
 * Run-time chain configuration
 */
export type ChainConfig = {
  PRESET_BASE: PresetName;
  /**
   * Free-form short name of the network that this configuration applies to - known
   * canonical network names include:
   * * 'mainnet' - there can be only one
   * * 'hoodi' - testnet
   * Must match the regex: [a-z0-9\-]
   */
  CONFIG_NAME: string;

  // Transition
  TERMINAL_TOTAL_DIFFICULTY: bigint;
  TERMINAL_BLOCK_HASH: Uint8Array;
  TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: number;

  // Genesis
  MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: number;
  MIN_GENESIS_TIME: number;
  GENESIS_FORK_VERSION: Uint8Array;
  GENESIS_DELAY: number;

  // Forking
  // Altair
  ALTAIR_FORK_VERSION: Uint8Array;
  ALTAIR_FORK_EPOCH: number;
  // Bellatrix
  BELLATRIX_FORK_VERSION: Uint8Array;
  BELLATRIX_FORK_EPOCH: number;
  // Capella
  CAPELLA_FORK_VERSION: Uint8Array;
  CAPELLA_FORK_EPOCH: number;
  // DENEB
  DENEB_FORK_VERSION: Uint8Array;
  DENEB_FORK_EPOCH: number;
  // ELECTRA
  ELECTRA_FORK_VERSION: Uint8Array;
  ELECTRA_FORK_EPOCH: number;
  // FULU
  FULU_FORK_VERSION: Uint8Array;
  FULU_FORK_EPOCH: number;
  // GLOAS
  GLOAS_FORK_VERSION: Uint8Array;
  GLOAS_FORK_EPOCH: number;

  // Time parameters
  /** @deprecated Use `SLOT_DURATION_MS` instead. */
  SECONDS_PER_SLOT: number;
  SLOT_DURATION_MS: number;
  SECONDS_PER_ETH1_BLOCK: number;
  MIN_VALIDATOR_WITHDRAWABILITY_DELAY: number;
  SHARD_COMMITTEE_PERIOD: number;
  ETH1_FOLLOW_DISTANCE: number;
  PROPOSER_REORG_CUTOFF_BPS: number;
  ATTESTATION_DUE_BPS: number;
  AGGREGATE_DUE_BPS: number;
  // Altair
  SYNC_MESSAGE_DUE_BPS: number;
  CONTRIBUTION_DUE_BPS: number;

  ATTESTATION_DUE_BPS_GLOAS: number;
  AGGREGATE_DUE_BPS_GLOAS: number;
  SYNC_MESSAGE_DUE_BPS_GLOAS: number;
  CONTRIBUTION_DUE_BPS_GLOAS: number;
  PAYLOAD_ATTESTATION_DUE_BPS: number;

  // Validator cycle
  INACTIVITY_SCORE_BIAS: number;
  INACTIVITY_SCORE_RECOVERY_RATE: number;
  EJECTION_BALANCE: number;
  MIN_PER_EPOCH_CHURN_LIMIT: number;
  MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: number;
  CHURN_LIMIT_QUOTIENT: number;
  MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: number;
  MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: number;

  // Fork choice
  PROPOSER_SCORE_BOOST: number;
  REORG_HEAD_WEIGHT_THRESHOLD: number;
  REORG_PARENT_WEIGHT_THRESHOLD: number;
  REORG_MAX_EPOCHS_SINCE_FINALIZATION: number;

  // Deposit contract
  DEPOSIT_CHAIN_ID: number;
  DEPOSIT_NETWORK_ID: number;
  DEPOSIT_CONTRACT_ADDRESS: Uint8Array;

  // Networking
  MAX_PAYLOAD_SIZE: number;
  MAX_REQUEST_BLOCKS: number;
  EPOCHS_PER_SUBNET_SUBSCRIPTION: number;
  MIN_EPOCHS_FOR_BLOCK_REQUESTS: number;
  ATTESTATION_PROPAGATION_SLOT_RANGE: number;
  MAXIMUM_GOSSIP_CLOCK_DISPARITY: number;
  MESSAGE_DOMAIN_INVALID_SNAPPY: Uint8Array;
  MESSAGE_DOMAIN_VALID_SNAPPY: Uint8Array;
  SUBNETS_PER_NODE: number;
  MAX_REQUEST_BLOCKS_DENEB: number;
  MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: number;
  MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: number;
  BLOB_SIDECAR_SUBNET_COUNT: number;
  MAX_BLOBS_PER_BLOCK: number;
  MAX_REQUEST_BLOB_SIDECARS: number;
  BLOB_SIDECAR_SUBNET_COUNT_ELECTRA: number;
  DATA_COLUMN_SIDECAR_SUBNET_COUNT: number;
  MAX_BLOBS_PER_BLOCK_ELECTRA: number;
  MAX_REQUEST_BLOB_SIDECARS_ELECTRA: number;
  MAX_REQUEST_DATA_COLUMN_SIDECARS: number;

  // Fulu
  NUMBER_OF_CUSTODY_GROUPS: number;
  SAMPLES_PER_SLOT: number;
  CUSTODY_REQUIREMENT: number;
  VALIDATOR_CUSTODY_REQUIREMENT: number;
  BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: number;

  // Gloas
  MAX_REQUEST_PAYLOADS: number;

  // Blob Scheduling
  BLOB_SCHEDULE: BlobSchedule;
};

export const chainConfigTypes: SpecTypes<ChainConfig> = {
  PRESET_BASE: "string",
  CONFIG_NAME: "string",

  // Transition
  TERMINAL_TOTAL_DIFFICULTY: "bigint",
  TERMINAL_BLOCK_HASH: "bytes",
  TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: "number",

  // Genesis
  MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: "number",
  MIN_GENESIS_TIME: "number",
  GENESIS_FORK_VERSION: "bytes",
  GENESIS_DELAY: "number",

  // Forking
  // Altair
  ALTAIR_FORK_VERSION: "bytes",
  ALTAIR_FORK_EPOCH: "number",
  // Bellatrix
  BELLATRIX_FORK_VERSION: "bytes",
  BELLATRIX_FORK_EPOCH: "number",
  // Capella
  CAPELLA_FORK_VERSION: "bytes",
  CAPELLA_FORK_EPOCH: "number",
  // DENEB
  DENEB_FORK_VERSION: "bytes",
  DENEB_FORK_EPOCH: "number",
  // ELECTRA
  ELECTRA_FORK_VERSION: "bytes",
  ELECTRA_FORK_EPOCH: "number",
  // FULU
  FULU_FORK_VERSION: "bytes",
  FULU_FORK_EPOCH: "number",
  // GLOAS
  GLOAS_FORK_VERSION: "bytes",
  GLOAS_FORK_EPOCH: "number",

  // Time parameters
  SECONDS_PER_SLOT: "number",
  SLOT_DURATION_MS: "number",
  SECONDS_PER_ETH1_BLOCK: "number",
  MIN_VALIDATOR_WITHDRAWABILITY_DELAY: "number",
  SHARD_COMMITTEE_PERIOD: "number",
  ETH1_FOLLOW_DISTANCE: "number",
  PROPOSER_REORG_CUTOFF_BPS: "number",
  ATTESTATION_DUE_BPS: "number",
  AGGREGATE_DUE_BPS: "number",
  // Altair
  SYNC_MESSAGE_DUE_BPS: "number",
  CONTRIBUTION_DUE_BPS: "number",

  ATTESTATION_DUE_BPS_GLOAS: "number",
  AGGREGATE_DUE_BPS_GLOAS: "number",
  SYNC_MESSAGE_DUE_BPS_GLOAS: "number",
  CONTRIBUTION_DUE_BPS_GLOAS: "number",
  PAYLOAD_ATTESTATION_DUE_BPS: "number",

  // Validator cycle
  INACTIVITY_SCORE_BIAS: "number",
  INACTIVITY_SCORE_RECOVERY_RATE: "number",
  EJECTION_BALANCE: "number",
  MIN_PER_EPOCH_CHURN_LIMIT: "number",
  MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: "number",
  CHURN_LIMIT_QUOTIENT: "number",
  MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: "number",
  MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: "number",

  // Fork choice
  PROPOSER_SCORE_BOOST: "number",
  REORG_HEAD_WEIGHT_THRESHOLD: "number",
  REORG_PARENT_WEIGHT_THRESHOLD: "number",
  REORG_MAX_EPOCHS_SINCE_FINALIZATION: "number",

  // Deposit contract
  DEPOSIT_CHAIN_ID: "number",
  DEPOSIT_NETWORK_ID: "number",
  DEPOSIT_CONTRACT_ADDRESS: "bytes",

  // Networking
  MAX_PAYLOAD_SIZE: "number",
  MAX_REQUEST_BLOCKS: "number",
  EPOCHS_PER_SUBNET_SUBSCRIPTION: "number",
  MIN_EPOCHS_FOR_BLOCK_REQUESTS: "number",
  ATTESTATION_PROPAGATION_SLOT_RANGE: "number",
  MAXIMUM_GOSSIP_CLOCK_DISPARITY: "number",
  MESSAGE_DOMAIN_INVALID_SNAPPY: "bytes",
  MESSAGE_DOMAIN_VALID_SNAPPY: "bytes",
  SUBNETS_PER_NODE: "number",
  MAX_REQUEST_BLOCKS_DENEB: "number",
  MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: "number",
  MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: "number",
  BLOB_SIDECAR_SUBNET_COUNT: "number",
  DATA_COLUMN_SIDECAR_SUBNET_COUNT: "number",
  MAX_BLOBS_PER_BLOCK: "number",
  MAX_REQUEST_BLOB_SIDECARS: "number",
  BLOB_SIDECAR_SUBNET_COUNT_ELECTRA: "number",
  MAX_BLOBS_PER_BLOCK_ELECTRA: "number",
  MAX_REQUEST_BLOB_SIDECARS_ELECTRA: "number",
  MAX_REQUEST_DATA_COLUMN_SIDECARS: "number",

  // Fulu
  NUMBER_OF_CUSTODY_GROUPS: "number",
  SAMPLES_PER_SLOT: "number",
  CUSTODY_REQUIREMENT: "number",
  VALIDATOR_CUSTODY_REQUIREMENT: "number",
  BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: "number",

  // Gloas
  MAX_REQUEST_PAYLOADS: "number",

  // Blob Scheduling
  BLOB_SCHEDULE: "blob_schedule",
};

export type BlobScheduleEntry = {
  EPOCH: number;
  MAX_BLOBS_PER_BLOCK: number;
};

export type BlobSchedule = BlobScheduleEntry[];

export function isBlobSchedule(value: unknown): value is BlobSchedule {
  return (
    Array.isArray(value) &&
    value.every(
      (entry) =>
        typeof entry === "object" &&
        entry !== null &&
        typeof entry.EPOCH === "number" &&
        typeof entry.MAX_BLOBS_PER_BLOCK === "number"
    )
  );
}

/** Allows values in a Spec file */
export type SpecValue = number | bigint | Uint8Array | string | BlobSchedule;

/** Type value name of each spec field. Numbers are ignored since they are the most common */
export type SpecValueType<V extends SpecValue> = V extends number
  ? "number"
  : V extends bigint
    ? "bigint"
    : V extends Uint8Array
      ? "bytes"
      : V extends string
        ? "string"
        : V extends BlobSchedule
          ? "blob_schedule"
          : never;

/** All possible type names for a SpecValue */
export type SpecValueTypeName = SpecValueType<SpecValue>;

export type SpecTypes<Spec extends Record<string, SpecValue>> = {
  [K in keyof Spec]: SpecValueType<Spec[K]>;
};

export type SpecJson = Record<string, string | Record<string, string>[]>;
